package com.yml.ktbus.core

import android.arch.lifecycle.*
import android.os.Handler
import android.os.Looper
import android.support.annotation.MainThread
import com.yml.ktbus.logger.DefaultLogger
import com.yml.ktbus.logger.Logger
import com.yml.ktbus.logger.LoggerManager
import com.yml.ktbus.utils.AppUtils
import com.yml.ktbus.utils.ThreadUtils
import java.util.logging.Level


class LiveEventBusCore private constructor() {

    // 单例
    companion object {
        private val instance: LiveEventBusCore by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { LiveEventBusCore() }
        fun get(): LiveEventBusCore = instance
    }

    /**
     * 存放LiveEvent
     */
    private val bus = mutableMapOf<String, LiveEvent<Any>>()

    /**
     * 可配置的项
     */
    private val config = Config()
    private var lifecycleObserverAlwaysActive = false
    private var autoClear = false
    private var logger: LoggerManager = LoggerManager(DefaultLogger())
    private val observableConfigs = mutableMapOf<String, ObservableConfig>()


    @Synchronized
    fun <T> with(key: String, type: Class<T>?): Observable<T>? {
        if (!bus.containsKey(key)) {
            bus[key] = LiveEvent(key)
        }
        return bus[key] as Observable<T>?
    }

    /**
     * 调试
     */
    fun getConsole(): InnerConsole {
        return InnerConsole()
    }

    fun setLogger(logger: Logger) {
        this.logger.logger = logger
    }

    fun enableLogger(enable: Boolean) {
        logger.isEnable = enable
    }

    fun setLifecycleObserverAlwaysActive(lifecycleObserverAlwaysActive: Boolean) {
        this.lifecycleObserverAlwaysActive = lifecycleObserverAlwaysActive
    }

    fun setAutoClear(autoClear: Boolean) {
        this.autoClear = autoClear
    }

    internal class ObserverWrapper<T> (private val observer: Observer<T>) :
        Observer<T?> {
        var preventNextEvent = false
        override fun onChanged(t: T?) {
            if (preventNextEvent) {
                preventNextEvent = false
                return
            }
            get().logger.log(Level.INFO, "message received: $t")
            try {
                observer.onChanged(t)
            } catch (e: ClassCastException) {
                get().logger.log(
                    Level.WARNING,
                    "class cast error on message received: $t", e
                )
            } catch (e: Exception) {
                get().logger.log(Level.WARNING, "error on message received: $t", e)
            }
        }
    }

    internal class LiveEvent<T> constructor(private val key: String) :
        Observable<T> {
        val liveData: LifecycleLiveData<T>
        private val observerMap: MutableMap<Observer<T>, ObserverWrapper<T>?> =
            HashMap()
        private val mainHandler = Handler(Looper.getMainLooper())

        /**
         * 进程内发送消息
         *
         * @param value 发送的消息
         */
        override fun post(value: T) {
            if (ThreadUtils.isMainThread) {
                postInternal(value)
            } else {
                mainHandler.post(PostValueTask(value))
            }
        }

        /**
         * 进程内发送消息，延迟发送
         *
         * @param value 发送的消息
         * @param delay 延迟毫秒数
         */
        override fun postDelay(value: T, delay: Long) {
            mainHandler.postDelayed(PostValueTask(value), delay)
        }

        /**
         * 进程内发送消息，延迟发送，带生命周期
         * 如果延时发送消息的时候sender处于非激活状态，消息取消发送
         *
         * @param owner 消息发送者
         * @param value 发送的消息
         * @param delay 延迟毫秒数
         */
        override fun postDelay(owner: LifecycleOwner?, value: T, delay: Long) {
            mainHandler.postDelayed(PostLifeValueTask(value, owner), delay)
        }

        /**
         * 进程内发送消息
         * 强制接收到消息的顺序和发送顺序一致
         *
         * @param value 发送的消息
         */
        override fun postOrderly(value: T) {
            mainHandler.post(PostValueTask(value))
        }

        /**
         * 注册一个Observer，生命周期感知，自动取消订阅
         *
         * @param owner    LifecycleOwner
         * @param observer 观察者
         */
        override fun observe(owner: LifecycleOwner, observer: Observer<T>) {
            if (ThreadUtils.isMainThread) {
                observeInternal(owner, observer)
            } else {
                mainHandler.post { observeInternal(owner, observer) }
            }
        }

        /**
         * 注册一个Observer，生命周期感知，自动取消订阅
         * 如果之前有消息发送，可以在注册时收到消息（消息同步）
         *
         * @param owner    LifecycleOwner
         * @param observer 观察者
         */
        override fun observeSticky(owner: LifecycleOwner, observer: Observer<T>) {
            if (ThreadUtils.isMainThread) {
                observeStickyInternal(owner, observer)
            } else {
                mainHandler.post { observeStickyInternal(owner, observer) }
            }
        }

        /**
         * 注册一个Observer，需手动解除绑定
         *
         * @param observer 观察者
         */
        override fun observeForever(observer: Observer<T>) {
            if (ThreadUtils.isMainThread) {
                observeForeverInternal(observer)
            } else {
                mainHandler.post { observeForeverInternal(observer) }
            }
        }

        /**
         * 注册一个Observer，需手动解除绑定
         * 如果之前有消息发送，可以在注册时收到消息（消息同步）
         *
         * @param observer 观察者
         */
        override fun observeStickyForever(observer: Observer<T>) {
            if (ThreadUtils.isMainThread) {
                observeStickyForeverInternal(observer)
            } else {
                mainHandler.post { observeStickyForeverInternal(observer) }
            }
        }

        /**
         * 通过observeForever或observeStickyForever注册的，需要调用该方法取消订阅
         *
         * @param observer 观察者
         */
        override fun removeObserver(observer: Observer<T>) {
            if (ThreadUtils.isMainThread) {
                removeObserverInternal(observer)
            } else {
                mainHandler.post { removeObserverInternal(observer) }
            }
        }

        @MainThread
        private fun postInternal(value: T) {
            get().logger.log(Level.INFO, "post: $value with key: $key")
            liveData.value = value
        }

        @MainThread
        private fun observeInternal(owner: LifecycleOwner, observer: Observer<T>) {
            val observerWrapper: ObserverWrapper<T> = ObserverWrapper(observer)
            observerWrapper.preventNextEvent = liveData.version > ExternalLiveData.START_VERSION
            liveData.observe(owner, observerWrapper)
            get().logger.log(
                Level.INFO, "observe observer: " + observerWrapper + "(" + observer + ")"
                        + " on owner: " + owner + " with key: " + key
            )
        }

        @MainThread
        private fun observeStickyInternal(owner: LifecycleOwner, observer: Observer<T>) {
            val observerWrapper: ObserverWrapper<T> = ObserverWrapper(observer)
            liveData.observe(owner, observerWrapper)
            get().logger.log(
                Level.INFO, "observe sticky observer: " + observerWrapper + "(" + observer + ")"
                        + " on owner: " + owner + " with key: " + key
            )
        }

        @MainThread
        private fun observeForeverInternal(observer: Observer<T>) {
            val observerWrapper: ObserverWrapper<T> = ObserverWrapper(observer)
            observerWrapper.preventNextEvent = liveData.version > ExternalLiveData.START_VERSION
            observerMap[observer] = observerWrapper
            liveData.observeForever(observerWrapper)
            get().logger.log(
                Level.INFO, "observe forever observer: " + observerWrapper + "(" + observer + ")"
                        + " with key: " + key
            )
        }

        @MainThread
        private fun observeStickyForeverInternal(observer: Observer<T>) {
            val observerWrapper: ObserverWrapper<T> = ObserverWrapper(observer)
            observerMap[observer] = observerWrapper
            liveData.observeForever(observerWrapper)
            get().logger.log(
                Level.INFO,
                "observe sticky forever observer: " + observerWrapper + "(" + observer + ")"
                        + " with key: " + key
            )
        }

        @MainThread
        private fun removeObserverInternal(observer: Observer<T>) {
            val realObserver: Observer<T> = if (observerMap.containsKey(observer)) {
                observerMap.remove(observer) as Observer<T>
            } else {
                observer
            }
            liveData.removeObserver(realObserver)
        }

        inner class LifecycleLiveData<T>(private val key: String) :
            ExternalLiveData<T>() {
            override fun observerActiveLevel(): Lifecycle.State {
                return if (lifecycleObserverAlwaysActive()) Lifecycle.State.CREATED else Lifecycle.State.STARTED
            }

            override fun removeObserver(observer: Observer<T>) {
                super.removeObserver(observer)
                if (autoClear() && !liveData.hasObservers()) {
                    get().bus.remove(key)
                }
                get().logger.log(Level.INFO, "observer removed: $observer")
            }

            private fun lifecycleObserverAlwaysActive(): Boolean {
                if (get().observableConfigs.containsKey(key)) {
                    val config: ObservableConfig? = get().observableConfigs[key]
                    if (config?.lifecycleObserverAlwaysActive != null) {
                        return config.lifecycleObserverAlwaysActive!!
                    }
                }
                return get().lifecycleObserverAlwaysActive
            }

            private fun autoClear(): Boolean {
                if (get().observableConfigs.containsKey(key)) {
                    val config: ObservableConfig? = get().observableConfigs[key]
                    if (config?.autoClear != null) {
                        return config.autoClear!!
                    }
                }
                return get().autoClear
            }
        }

        private inner class PostValueTask(private val newValue: T) : Runnable {
            override fun run() {
                postInternal(newValue)
            }
        }

        private inner class PostLifeValueTask(
            private val newValue: T,
            private val owner: LifecycleOwner?
        ) :
            Runnable {
            override fun run() {
                if (owner != null) {
                    if (owner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
                        postInternal(newValue)
                    }
                }
            }
        }

        init {
            liveData = LifecycleLiveData(key)
        }

    }

    inner class InnerConsole {
        /**
         * 获取控制台信息
         *
         * @return 调试信息
         */
        val consoleInfo: String
            get() {
                val sb = StringBuilder()
                sb.append("*********Base info*********").append("\n")
                sb.append(baseInfo)
                sb.append("*********Event info*********").append("\n")
                sb.append(busInfo)
                return sb.toString()
            }

        val baseInfo: String
            get() {
                val sb = StringBuilder()
                sb.append("lifecycleObserverAlwaysActive: ").append(lifecycleObserverAlwaysActive)
                    .append("\n")
                    .append("autoClear: ").append(autoClear).append("\n")
                    .append("logger enable: ").append(logger.isEnable).append("\n")
                    .append("logger: ").append(logger.logger).append("\n")
                    .append("Application: ").append(AppUtils.app).append("\n")
                return sb.toString()
            }

        val busInfo: String
            get() {
                val sb = StringBuilder()
                for (key in bus.keys) {
                    sb.append("Event name: $key").append("\n")
                    val liveData: LiveEvent<Any>.LifecycleLiveData<Any>? = bus[key]?.liveData
                    sb.append("\tversion: " + liveData?.version).append("\n")
                    sb.append("\thasActiveObservers: " + liveData?.hasActiveObservers()).append("\n")
                    sb.append("\thasObservers: " + liveData?.hasObservers()).append("\n")
                    sb.append("\tActiveCount: " + liveData?.let { getActiveCount(it) }).append("\n")
                    sb.append("\tObserverCount: " + liveData?.let { getObserverCount(it) }).append("\n")
                    sb.append("\tObservers: ").append("\n")
                    sb.append("\t\t" + liveData?.let { getObserverInfo(it) }).append("\n")
                }
                return sb.toString()
            }

        private fun getActiveCount(liveData: LiveData<*>): Int {
            return try {
                val field = LiveData::class.java.getDeclaredField("mActiveCount")
                field.isAccessible = true
                field[liveData] as Int
            } catch (e: java.lang.Exception) {
                -1
            }
        }

        private fun getObserverCount(liveData: LiveData<*>): Int {
            return try {
                val field = LiveData::class.java.getDeclaredField("mObservers")
                field.isAccessible = true
                val mObservers = field[liveData]
                val classOfSafeIterableMap: Class<*> = mObservers.javaClass
                val size = classOfSafeIterableMap.getDeclaredMethod("size")
                size.isAccessible = true
                size.invoke(mObservers) as Int
            } catch (e: java.lang.Exception) {
                -1
            }
        }

        private fun getObserverInfo(liveData: LiveData<*>): String {
            return try {
                val field = LiveData::class.java.getDeclaredField("mObservers")
                field.isAccessible = true
                val mObservers = field[liveData]
                mObservers.toString()
            } catch (e: java.lang.Exception) {
                ""
            }
        }
    }

}